﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using OpenTap.Plugins.Interfaces.Common;
using OpenTap.Plugins.Interfaces.MultiModeInstrument;
using OpenTap.Plugins.Interfaces.SG;

namespace OpenTap.Plugins.Sg
{
    [Display("Rohde&Schwarz Smw driver", Group: "OpenTap.Plugins", Description: "Rohde & Schwarz Signal generator")]
    public class SgRohdeSchwarzSmw : ScpiInstrument, ISg
    {
        protected readonly Ftp _ftp;

        public SgRohdeSchwarzSmw()
        {
            _ftp = new Ftp();
            ArbFileList = new List<string> {"ArbOne"};
            Name = "SMW";
        }

        /// <summary>
        ///     Open procedure for the instrument.
        /// </summary>
        public override void Open()
        {
            base.Open();
            SetReferenceClockSource(ClockState);
            CheckIfArbFileExists(UseArbFileCheck);
            IoTimeout = 20000;
        }

        /// <summary>
        ///     Close procedure for the instrument.
        /// </summary>
        public override void Close()
        {
            base.Close();
        }

        /// <inheritdoc />
        public void SetFrequency(double freqInMHz)
        {
            if (freqInMHz <= 0) throw new ArgumentOutOfRangeException(nameof(freqInMHz));
            ScpiCommand("FREQ " + (freqInMHz * 1E6).ToString("#.000000", CultureInfo.InvariantCulture) + ";*WAI");
        }

        /// <inheritdoc />
        public decimal GetFixedFrequency()
        {
            Log.Warning("Not supported on R&S SMW");

            return decimal.MinValue;
        }

        /// <inheritdoc />
        public decimal GetFrequency()
        {
            return ScpiQuery<decimal>("FREQ?;*WAI");
        }

        /// <inheritdoc />
        public void SetOutputLevel(double outputLevelInDbm)
        {
            ScpiCommand("SOUR:POW " + outputLevelInDbm.ToString("#.000", CultureInfo.InvariantCulture) + ";*WAI");
        }

        /// <inheritdoc />
        public double GetOutputLevel()
        {
            return ScpiQuery<double>("SOUR:POW?" + ";*WAI");
        }

        /// <inheritdoc />
        public void SetRfOutputState(EState outputState)
        {
            ScpiCommand("OUTP:STAT " + Scpi.Format("{0}", outputState) + ";*WAI");
        }

        /// <inheritdoc />
        public virtual void SetModulationState(EState modulationState)
        {
            ScpiCommand("SOUR:MOD:ALL:STAT " + Scpi.Format("{0}", modulationState) + ";*WAI");
        }

        /// <inheritdoc />
        public void SetAlcState(EState state)
        {
            Log.Warning("Not supported on R&S SMW");
        }

        /// <inheritdoc />
        public void SetWidebandIqModulatorState(EState modulatorState)
        {
            Log.Warning("Not supported on R&S SMW");
        }

        /// <inheritdoc />
        public void SetReferenceOscillatorAutoState(EState oscillatorState)
        {
            Log.Warning("Not supported on R&S SMW");
        }

        /// <inheritdoc />
        public void SetReferenceClock(double refFreqInMhz, EArbRefSource referenceSource)
        {
            ScpiCommand("ROSC:SOUR " + Scpi.Format("{0}", referenceSource) + ";*WAI");
            if (referenceSource == EArbRefSource.RefsourceExt)
                ScpiCommand("ROSC:EXT:FREQ " + refFreqInMhz * 1E6 + ";*WAI");
        }

        /// <inheritdoc />
        public virtual void SetSelectArbFile(string fileFolderAndName, double awgSampleClockMhz)
        {
            if (fileFolderAndName != string.Empty)
            {
                // load does not work with long filename like: "/var/user/5G_SIGNALS/64QAM_cellid1_papr13"
                ScpiCommand("BB:ARB:WAV:SEL \"" + fileFolderAndName + "\";*WAI");
                ScpiCommand("SOUR:BB:ARB:STAT ON;*WAI");
            }
            else
            {
                throw new ArgumentException("folder or patternName is empty!");
            }
        }

        /// <inheritdoc />
        public uint GetPowerCondition()
        {
            Log.Warning("Not supported on R&S SMW");

            return uint.MinValue;
        }

        public virtual void SetTriggerPolarity(ETriggerPolarity polarity)
        {
            Log.Info("Sg:Set trigger polarity");
            if (ETriggerPolarity.PolarityNeg == polarity)
                ScpiCommand("INP:TRIG:BBAND:SLOP NEG;*WAI");
            else
                ScpiCommand("INP:TRIG:BBAND:SLOP POS;*WAI");
        }

        public void SetTriggerDelay(double delayMs)
        {
            if (delayMs <= 0)
                throw new ArgumentException("Trying to set trigger delay <= 0 !", "delayMs");
            Log.Info("Sg:Set trigger delay");
            ScpiCommand("BB:ARB:TRIG:DEL " + delayMs.ToString(CultureInfo.InvariantCulture) + ";*WAI");
        }

        public void SetArbTriggerSourceExt()
        {
            Log.Info("Sg:Set Arb trigger source to external");
            ScpiCommand("BB:ARB:TRIG:SOUR EXT;*WAI");
        }

        public void SetArbState(EState arbState)
        {
            Log.Info("Sg:Set Arb State " + arbState);
            ScpiCommand("SOUR:BB:ARB:STAT " + Scpi.Format("{0}", arbState));
        }

        /// <inheritdoc />
        public EopcStatus Opc()
        {
            var opcStatus = ScpiQuery("*OPC?");
            return opcStatus.Equals("1") ? EopcStatus.Complete : EopcStatus.Incomplete;
        }

        public void SetRuntimeScaling(double scale)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        ///     Set clock source to ext/int
        /// </summary>
        /// <param name="clockState">Clock state</param>
        private void SetReferenceClockSource(EState clockState)
        {
            ScpiCommand(EState.On == clockState ? "ROSC:SOUR EXT" : "ROSC:SOUR INT");
        }

        protected virtual void CheckIfArbFileExists(EState arbFileCheckState)
        {
            const string ftpFolder = "\\share\\";
            const string scpiFolder = "\\var\\user\\";
            if (arbFileCheckState != EState.On) return;
            foreach (var arb in ArbFileList)
                if (arb == "ArbOne")
                {
                    Log.Info("SMW200: Empty arbfile list");
                }

                else
                {
                    ScpiCommand("MMEM:CDIR \"" + scpiFolder + ArbFileFolder + "\";*WAI");
                    //TODO: Can made little bit faster if qurey is outside loop
                    var instrumentFiles = ScpiQuery(":MMEM:CAT?");
                    if (instrumentFiles.Contains(arb)) continue;
                    Log.Info("SMW200:Need to transfer file from PC.File:  " + arb);
                    var address = VisaAddress.Remove(0, 8); //"TCPI0::"
                    //192.168.255.xxx =maxLenght
                    const int maxLenght = 15;
                    address = string.Concat(address.Take(maxLenght));
                    var ftpArbFolderAndFileName = ftpFolder + ArbFileFolder + "/" + arb;

                    if (!_ftp.DirectoryExists(address,
                        FtpUserName,
                        FtpPassword, ftpFolder + ArbFileFolder))
                    {
                        _ftp.CreateDirectory(address,
                            FtpUserName,
                            FtpPassword, ftpFolder + ArbFileFolder);
                    }

                    _ftp.SendFile(address,
                        FtpUserName,
                        FtpPassword, false,
                        ftpArbFolderAndFileName,
                        LocalArbFileFolder + "\\" +
                        arb);
                }
        }

        #region Settings

        [Display("Clock source external", "Set clock source external", null, 1)]
        public EState ClockState { get; set; }

        [Display("Arb file check", "Use arb file check on instrument", null, 2)]
        public EState UseArbFileCheck { get; set; }

        [Display("Ftp username", "Ftp user name for arb file transfer", null, 3)]
        public string FtpUserName { get; set; }

        [Display("Ftp password", "Ftp password for arb file transfer", null, 4)]
        public string FtpPassword { get; set; }

        [Display("Instrument arb folder", "Instrument arb file folder (Last folder name)", null, 5)]
        public string ArbFileFolder { get; set; }

        [Display("Local arb folder", "PC arb file folder", null, 7)]
        public string LocalArbFileFolder { get; set; }

        [Display("Arb files list", "List of used arb files", null, 8)]
        public List<string> ArbFileList { get; set; }

        public List<string> ArbFiles
        {
            get => ArbFileList;
            set
            {
                ArbFileList = value;
                OnPropertyChanged("ArbFiles");
            }
        }

        #endregion

        #region Implementation of IScpi

        public void Clear()
        {
            ScpiCommand("*CLS");
        }

        #endregion

        #region Implementation of IMultiModeInstrument

        public void SetInstrumentMode(EInstrumentMode instrumentMode)
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}